#include <amxmodx>
#include <amxmisc>
#include <fakemeta>

#define PLUGIN "Shoot Weapons (Weapon Physics)"
#define VERSION "1.0"
#define AUTHOR "Nomexous"

/*

This plugin requires Weapon Physics to work. Get it here: http://forums.alliedmods.net/showthread.php?t=66472

For an explanation of my method, please go to the end of the plugin.

Version 1.0
 - Initial release.

*/

new fid = 0

public plugin_init()
{
	register_plugin(PLUGIN, VERSION, AUTHOR)
	
	fid = register_forward(FM_TraceLine, "fw_traceline")
	
	register_concmd("amx_shoot_weapons", "concmd", ADMIN_SLAY, "Turn the shoot weapons command on or off.")
}

public concmd(id, level, cid)
{
	if (!cmd_access(id, level, cid, 1)) return PLUGIN_HANDLED
	
	new cmd[3]
	read_argv(1, cmd, 2)
	
	if (str_to_num(cmd))
	{
		if (!fid)
		{
			fid = register_forward(FM_TraceLine, "fw_traceline")
			client_print(id, print_console, "Shooting of weapons enabled.")
		}
		else
		{
			client_print(id, print_console, "Shooting of weapons already enabled.")
		}
	}
	else
	{
		if (fid)
		{
			unregister_forward(FM_TraceLine, fid)
			fid = 0
			client_print(id, print_console, "Shooting of weapons disabled.")
		}
		else
		{
			client_print(id, print_console, "Shooting of weapons also disabled.")
		}
	}
	
	return PLUGIN_HANDLED
}

public fw_traceline(Float:start[3], Float:end[3], conditions, id, trace)
{
	// Spectators don't need to run this.
	if (!is_user_alive(id)) return FMRES_IGNORED
	
	// If we hit a player, don't bother searching for an item nearby.
	if (is_user_alive(get_tr2(trace, TR_pHit))) return FMRES_IGNORED
	
	static Float:endpt[3], tr = 0, i
	
	get_tr2(trace, TR_vecEndPos, endpt)
	
	i = 0
	
	while ((i = engfunc(EngFunc_FindEntityInSphere, i, endpt, 20.0)))
	{
		if (is_shootable_ent(i))
		{
			engfunc(EngFunc_TraceModel, start, end, HULL_POINT, i, tr)
		
			if (pev_valid(get_tr2(tr, TR_pHit)))
			{
				get_tr2(tr, TR_vecEndPos, endpt)
				set_tr2(trace, TR_vecEndPos, endpt)
				
				set_tr2(trace, TR_pHit, i)
				
				return FMRES_IGNORED
			}
		}
	}
	
	return FMRES_IGNORED
}

public is_shootable_ent(ent)
{
	if (!pev_valid(ent)) return false
	
	static classname[32]
	pev(ent, pev_classname, classname, 31)
	static szEntModel[32];
	pev( ent , pev_model , szEntModel , 31 );

	if( equal( szEntModel , "models/w_backpack.mdl" ) )
	{
			return false;
	}
	if (equal(classname, "weaponbox") || equal(classname, "armoury_entity") || equal(classname, "weapon_shield") || equal(classname, "item_thighpack"))
	{
		return true
	}
	return false
}

/*

Here's the difficulty in shooting weapons: they don't block traceline. If they can't block traceline, that means
they can't register as a hit. My method currently works by hooking traceline and checking for and items within a
certain radius of the endpoint. If it does find an item, a tracemodel is run. It goes the same path as the
traceline does, and sees if it hits the item. If it does, it alters the trace result to show that it's hitting
the item. The Weapon Physics plugin will handle it from there.

The one thing that irks me is that a "while" loop has to be run in the traceline forward. I'm not sure how
expensive that "while" loop actually is, but it can't be good, because traceline is called constantly. Not to
mention we have to find the classname of each item found each FindEntityInSphere. Maybe it isn't as CPU intensive
as I'm thinking it is, but there's definitely an increase.

EngFunc_Tracemodel is not well documented, so I'm doing it here for future reference:

engfunc(EngFunc_TraceModel, const Float:start[3], const Float:end[3], hull, ent_to_hit, traceresult)

start[3]    - Where to start the trace.
end[3]      - Where to trace to.
hull        - Takes the HULL_* constants. Basically, you're moving a certain sized object along the line until you
              collides with something. HULL_POINT moves a single point from start[3] to end[3] (making it almost
	      like traceline). HULL_HEAD moves a ducking player-sized hull along from start[3] to end[3] until
	      part of that hull hits something. HULL_HUMAN is a standing person, and HULL_LARGE I have no idea.
ent_to_hit  - The entity you want to hit. Tracemodel will ignore everything else. Don't set this to 0 or -1; you'll
              crash the server.
traceresult - Our trace result handle. Use get_tr2() and set_tr2().

*/
